/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org

This file is part of Gephi.

Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.graph.api;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Main interface for accessing the graph structure and develop algorithms.
 * <p>
 * A graph object belongs to a graph model, which really contains the data. Graph
 * objects are therefore only accessors, with all convinient methods to read and
 * modify the structure. Hence, <b>multiple</b> <code>Graph</code> objects can
 * exists.
 * <h3>Get current graph</h3>
 * <pre>
 * GraphController graphController = Lookup.getDefault().lookup(GraphController.class);
 * GraphModel model = graphController.getModel();
 * Graph graph = model.getGraph();
 * </pre>
 * <b>Note:</b> This shows how to get the complete graph, for getting only the
 * currently visualized graph, call <code>model.getGraphVisible()</code> instead.
 * <h3>Graph Locking</h3>
 * Graph structure possess a locking mechanism that avoids multiple threads
 * to modify the structure concurrently. However, several readers are allowed.
 * <p>
 * There is two different way to perform read locking:
 * <ul><li>Manually by calling <code>readLock()</code> and <code>readUnlock()</code></li>
 * <li>Structure is automatically locked when you read the various <code>NodeIterable</code>
 * and <code>EdgeIterable</code></li></ul>
 * Be always sure you don't leave the structure blocked. use <code>readUnlockAll()</code>
 * to release all the locks.
 * <p>
 * <b>Note:</b> Write locking is automatically done when modifying th graph
 * (add, remove, ...).
 * @author Mathieu Bastian
 */
public interface Graph {

    /**
     * Add <code>edge</code> to the graph. Graph does not accept parallel edges.
     * Fails if <code>edge</code> is already in the graph.
     * @param edge the edge to add
     * @return true if add is successful, false otherwise
     * @throws IllegalArgumentException if <code>edge</code> is <code>null</code>,
     * or if nodes are not legal nodes for this <code>edge</code>,
     * or if <code>edge</code> is directed when the graph is undirected,
     * or if <code>edge</code> is undirected when the graph is directed,
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public boolean addEdge(Edge edge);

    /**
     * Add a node to the graph.
     * Fails if the node already exists.
     * @param node the node to add
     * @return true if add is successful, false otherwise
     * @throws IllegalArgumentException if <code>node</code> is <code>null</code>
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public boolean addNode(Node node);

    /**
     * Remove <code>edge</code> from the graph.
     * Fails if the edge doesn't exist.
     * @param edge the edge to remove
     * @return true if remove is successful, false otherwise
     * @throws IllegalArgumentException if <code>edge</code> is <code>null</code> or nodes not legal in
     * the graph
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public boolean removeEdge(Edge edge);

    /**
     * Remove <code>node</code> from the graph. All <code>node</code>'s children and incident edges will
     * also be removed.
     * @param node the node to remove
     * @return true if remove is successful, false otherwise
     * @throws IllegalArgumentException if <code>node</code> is <code>null</code>
     * or not legal in the graph.
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public boolean removeNode(Node node);

    /**
     * Returns true if the graph contains <code>node</code>.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param node the node whose presence is required
     * @return true if the graph contains <code>node</code>
     * @throws IllegalArgumentException if <code>node</code> is <code>null</code>
     */
    public boolean contains(Node node);

    /**
     * Returns true if the graph contains <code>edge</code>.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param edge the edge whose presence is required
     * @return true if the graph contains <code>edge</code>
     * @throws IllegalArgumentException if <code>edge</code> is <code>null</code>
     */
    public boolean contains(Edge edge);

    /**
     * Returns the node with identifier equals to <code>id</code>. If not found,
     * returns <code>null</code>. This id is generated and can be found in
     * <code>Node.getId()</code>.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param id a positive number
     * @return the node with given <code>id</code>, or <code>null</code> if not found
     */
    public Node getNode(int id);

    /**
     * Returns the node with identifier equals to <code>id</code>. If not found,
     * returns <code>null</code>. This id is set by user and can be found at
     * <code>NodeData.getId()</code>. To set this Id, use <code>Graph.setId()</code>.
     * <p>
     * The node must be present in the view this graph is from.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param id the string identifier, unique
     * @return the node with given <code>id</code>, or <code>null</code> if not found
     */
    public Node getNode(String id);

    /**
     * Returns the edge with identifier equals to <code>id</code>. If not found,
     * returns <code>null</code>. This id is generated and can be found in
     * <code>Edge.getId()</code>.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param id a positive number
     * @return the edge with given <code>id</code>, or <code>null</code> if not found
     */
    public Edge getEdge(int id);

    /**
     * Returns the edge with identifier equals to <code>id</code>. If not found,
     * returns <code>null</code>.  This id is set by user and can be found at
     * <code>EdgeData.getId()</code>. To set this Id, use <code>Graph.setId()</code>.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param id the string identifier, unique
     * @return the edge with given <code>id</code>, or <code>null</code> if not found
     */
    public Edge getEdge(String id);

    /**
     * Finds and returns a <b>directed</b> or <b>undirected</b> edge that connects <code>node1</code> and
     * <code>node2</code>. Returns <code>null</code> if no such edge is found.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param node1 the first incident node of the queried edge
     * @param node2 thge second incident node of the queried edge
     * @return an edge that connects <code>node1</code> and <code>node2</code>
     * or <code>null</code> if no such edge exists
     * @throws IllegalArgumentException if <code>node1</code> or <code>node2</code>
     * are <code>null</code> or not legal nodes in the graph
    */
    public Edge getEdge(Node node1, Node node2);

    /**
     * Returns nodes contained in the graph.
     * @return a node iterable of nodes contained in the graph.
     */
    public NodeIterable getNodes();

    /**
     * Returns edges contained in the graph. Self-loops will be present only once.
     * <p>
     * If the graph is <b>undirected</b>, directed mutual edges will be present only once.
     * @return an edge iterable of edges contained in the graph.
     */
    public EdgeIterable getEdges();

    /**
     * Returns neighbors of <code>node</code>. Neighbors are nodes connected to
     * <code>node</code> with any edge of the graph. Neighbors exclude <code>node</code> itself,
     * therefore self-loops are ignored.
     * @param node the node whose neighbors are to be returned
     * @return a node iterable of <code>node</code>'s neighbors
     * @throws IllegalArgumentException if <code>node</code> is <code>null</code>
     * or not legal in the graph.
     */
    public NodeIterable getNeighbors(Node node);

    /**
     * Returns edges incident to <code>node</code>.
     * <p>
     * For <b>directed</b> graph, note that self-loops are repeated only once. <b>Undirected</b>
     * graphs repeats edges once by default.
     * @param node the node whose incident edges are to be returned
     * @return an edge iterable of edges incident to <code>node</code>
     * @throws IllegalArgumentException if <code>node</code> is <code>null</code>
     * or not legal in the graph.
     */
    public EdgeIterable getEdges(Node node);

    /**
     * Returns the number of nodes in the graph.
     * <p>
     * Special case of interest:
     * <ul><li>For <b>hierarchical</b> graph, returns the number of nodes in the
     * current view only.</li>
     * </ul>
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @return the number of nodes in the graph
     */
    public int getNodeCount();

    /**
     * Returns the number of edges in the graph
     * <p>
     * Special case of interest:
     * <ul><li>Count self-loops once only.</li>
     * <li>For <b>hierarchical</b> graph, count edges incident only to nodes
     * in the current view.</li></ul>
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @return the number of edges in the graph.
     */
    public int getEdgeCount();

    /**
     * Return the current node version of the graph. When node structure is touched (i.e. add, remove...) the
     * node version is <b>incremented</b>.
     * <p>External modules can compare local copy of version to determine if update
     * is necessary.
     * @return the current node version of the graph
     */
    public int getNodeVersion();

    /**
     * Return the current edge version of the graph. When edge structure is touched (i.e. add, remove...) the
     * edge version is <b>incremented</b>.
     * <p>External modules can compare local copy of version to determine if update
     * is necessary.
     * @return the current edge version of the graph
     */
    public int getEdgeVersion();

    /**
     * Returns the adjacent node of <code>node</code> through <code>edge</code>.
     * @param node the node whose adjacent node is to be returned
     * @param edge the edge whose the opposite node is to be returned
     * @return the adjacent node of <code>node</code> through <code>edge</code>
     * @throws IllegalArgumentException if <code>node</code> or <code>edge</code> is <code>null</code>,
     * or if <code>node</code> is not incident to <code>edge</code>
     */
    public Node getOpposite(Node node, Edge edge);

    /**
     * Returns the degree of <code>node</code>. Self-loops are counted twice for <b>directed</b> graphs.
     * @param node the node whose degree is to be returned
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @return the degree of <code>node</code>
     * @throws IllegalArgumentException if <code>node</code> is <code>null</code> or not legal in the graph
     */
    public int getDegree(Node node);

    /**
     * Returns <code>true</code> if <code>edge</code> is a self-loop.
     * @param edge the edge to be queried
     * @return <code>true</code> if <code>edge</code> is a self-loop, <code>false</code> otherwise
     * @throws IllegalArgumentException if <code>edge</code> is <code>null</code> or adjacent nodes not
     * legal in the graph
     */
    public boolean isSelfLoop(Edge edge);

    /**
     * Returns <code>true</code> if <code>edge</code> is a directed edge in the current graph. Always
     * returns <code>true</code> when the graph is <b>directed</b> and <code>false</code> when the graph
     * is <b>undirected</b>. In case of a <b>mixed</b> graph returns </code>Edge.isDirected()</code>.
     * @param edge
     * @return <code>true</code> is <code>edge</code> is directed
     * @throws IllegalArgumentException if <code>edge</code> is <code>null</code> or adjacent nodes not
     * legal in the graph
     */
    public boolean isDirected(Edge edge);

    /**
     * Returns <code>true</code> if <code>node1</code> is adjacent to <code>node2</code>. Is adjacent
     * when an edge exists between <code>node1</code> and <code>node2</code>.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param node1 the first node to be queried
     * @param node2 the seconde node to be queried
     * @return <code>true</code> if <code>node1</code> is adjacent to <code>node2</code>
     * @throws IllegalArgumentException if <code>node1</code> or <code>node2</code> is <code>null</code> of
     * not legal in the graph
     */
    public boolean isAdjacent(Node node1, Node node2);

    /**
     * Returns <code>true</code> if <code>edge1</code> is adjacent to <code>edge2</code>. Is adjacent
     * when an node is incident to both edges.
     * <p><b>Warning:</b> This method is not thread safe, be sure to call it in a locked
     * statement.
     * @param edge1 the first node to be queried
     * @param edge2 the seconde node to be queried
     * @return <code>true</code> if <code>edge1</code> is adjacent to <code>edge2</code>
     * @throws IllegalArgumentException if <code>edge1</code> or <code>edge2</code> is <code>null</code> of
     * not legal in the graph
     */
    public boolean isAdjacent(Edge edge1, Edge edge2);

    /**
     * Removes all edges incident to <code>node</code>.
     * @param node the node whose edges is to be cleared
     * @throws IllegalArgumentException if <code>node</code> if null or not legal in the graph
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public void clearEdges(Node node);

    /**
     * Removes all nodes and edges in the graph.
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public void clear();

    /**
     * Removes all edges in the graph.
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public void clearEdges();

    /**
     * Sets the string identifier of <code>node</code>. This identifier can be set
     * by users, in contrario of {@link Node#getId()} which is set by the system.
     * @param id            the id that is to be set for <code>node</code>
     */
    public void setId(Node node, String id);

    /**
     * Sets the string identifier of <code>edge</code>. This identifier can be set
     * by users, in contrario of {@link Edge#getId()} which is set by the system.
     * @param id            the id that is to be set for <code>edge</code>
     */
    public void setId(Edge edge, String id);

    /**
     * Acquires a read lock on the graph. Calling thread will be blocked until all write locks are released.
     * Several threads can read but only once can write.
     * <p>
     * A read lock can be acquired several times by a thread, but be sure to call <code>readUnlock()</code>
     * the same number of time you called this method, or use <code>readUnlockAll()</code> to release all
     * locks.
     * @see ReentrantReadWriteLock
     */
    public void readLock();

    /**
     * Releases the read lock on the graph. Must be called from the same thread that locked the graph.
     * <p>
     * Use <code>readUnlockAll()</code> if you ignore the number of times the read lock has been acquired.
     */
    public void readUnlock();

    /**
     * Safe method that releases all read locks the calling thread has acquired. Use this method if you are
     * cancelling a task and you don't know how many read locks have been acquired.
     */
    public void readUnlockAll();

    /**
     * Acquires a write lock on the graph. Calling thread will be blocked until all read locks are released.
     * Several threads can read but only once can write.
     * @see ReentrantReadWriteLock
     * @throws IllegalMonitorStateException if the current thread is holding a read lock
     */
    public void writeLock();

    /**
     * Release the write lock on the graph. Must be called from the same thread that locked the graph.
     */
    public void writeUnlock();

    /**
     * Returns the graph model this graph belongs to.
     * @return the graph model this graph belongs to.
     */
    public GraphModel getGraphModel();

    /**
     * Returns the graph view this graph belongs to.
     * @return the graph view this graph belongs to.
     */
    public GraphView getView();
}
